ostree-prepare-root: Fix running with musl
authorWilliam Manley <will@williammanley.net>
Wed, 31 Aug 2016 16:15:48 +0000 (17:15 +0100)
committerAtomic Bot <atomic-devel@projectatomic.io>
Thu, 1 Sep 2016 20:17:58 +0000 (20:17 +0000)
musl libc's implementation of `realpath` works by opening the path and then
doing a lookup in `/proc/self/fd` to find the canonical path.  This fails
if `/proc` is not mounted.  This causes problems for us if
`ostree-prepare-root` is `init` as `/proc` won't be mounted.

We have to mount `/proc` anyway for `/proc/cmdline` so this fix just
expands the scope over which `/proc` is mounted to include both our
`realpath` calls.

See also:

* http://www.openwall.com/lists/musl/2016/06/08/2 and
* http://git.musl-libc.org/cgit/musl/tree/src/misc/realpath.c?id=e738b8cbe64b6dd3ed9f47b6d4cd7eb2c422b38d

Closes: #485
Approved by: cgwalters

src/switchroot/ostree-prepare-root.c
tests/test-switchroot.sh

index ea7333b56eba6dea447cf6c132d2637c5e15ff23..ce48a91ef3aae69a19a439a42d2ed855649c1f2e 100644 (file)
@@ -80,26 +80,10 @@ parse_ostree_cmdline (void)
   char *cmdline = NULL;
   const char *iter;
   char *ret = NULL;
-  int tmp_errno;
 
   cmdline = read_proc_cmdline ();
   if (!cmdline)
-    {
-      // Mount proc
-      if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
-        err (EXIT_FAILURE, "failed to mount proc on /proc");
-
-      cmdline = read_proc_cmdline ();
-      tmp_errno = errno;
-
-      /* Leave the filesystem in the state that we found it: */
-      if (umount ("/proc"))
-        err (EXIT_FAILURE, "failed to umount proc from /proc");
-
-      errno = tmp_errno;
-      if (!cmdline)
-        err (EXIT_FAILURE, "failed to read /proc/cmdline");
-    }
+    err (EXIT_FAILURE, "failed to read /proc/cmdline");
 
   iter = cmdline;
   while (iter != NULL)
@@ -178,17 +162,36 @@ main(int argc, char *argv[])
   char *deploy_path = NULL;
   char srcpath[PATH_MAX];
   struct stat stbuf;
+  int we_mounted_proc = 0;
 
   if (argc < 2)
     root_arg = "/";
   else
     root_arg = argv[1];
 
+  if (stat ("/proc/cmdline", &stbuf) < 0)
+    {
+      if (errno != ENOENT)
+        err (EXIT_FAILURE, "stat(\"/proc/cmdline\") failed");
+      /* We need /proc mounted for /proc/cmdline and realpath (on musl) to
+       * work: */
+      if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
+        err (EXIT_FAILURE, "failed to mount proc on /proc");
+      we_mounted_proc = 1;
+    }
+
   root_mountpoint = realpath (root_arg, NULL);
   if (root_mountpoint == NULL)
     err (EXIT_FAILURE, "realpath(\"%s\")", root_arg);
   deploy_path = resolve_deploy_path (root_mountpoint);
 
+  if (we_mounted_proc)
+    {
+      /* Leave the filesystem in the state that we found it: */
+      if (umount ("/proc"))
+        err (EXIT_FAILURE, "failed to umount proc from /proc");
+    }
+
   /* Work-around for a kernel bug: for some reason the kernel
    * refuses switching root if any file systems are mounted
    * MS_SHARED. Hence remount them MS_PRIVATE here as a
index 96c849e54595721e941b875d7fac8a99af22c128..fd5a30d05fc8da3d7f4e7e35893be8bced579f31 100755 (executable)
@@ -4,7 +4,14 @@ this_script="${BASH_SOURCE:-$(readlink -f "$0")}"
 
 setup_bootfs() {
        mkdir -p "$1/proc" "$1/bin"
-       echo "quiet ostree=/ostree/boot.0 ro" >"$1/proc/cmdline"
+
+       # We need the real /proc mounted here so musl's realpath will work, but we
+       # want to be able to override /proc/cmdline, so bind mount.
+       mount -t proc proc "$1/proc"
+
+       echo "quiet ostree=/ostree/boot.0 ro" >"$1/override_cmdline"
+       mount --bind "$1/override_cmdline" "$1/proc/cmdline"
+
        touch "$1/this_is_bootfs"
        cp "$(dirname "$this_script")/../ostree-prepare-root" "$1/bin"
 }